package com.gitee.tianchaohongyu.jdcl.util;

import com.gitee.tianchaohongyu.jdcl.exception.ReflectWeakException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.lang.Nullable;

import javax.validation.constraints.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * 反射工具类
 */
public class ReflectUtils {

  private static final Logger logger = LoggerFactory.getLogger(ReflectUtils.class);

  /**
   * 获取某类对应父类上的泛型列表
   *
   * @param clazz      要获取泛型列表的类
   * @param superClass 泛型相对于的父类
   * @param <S>        父类类型
   * @param <T>        类型
   * @return 返回该类泛型列表中相对于指定父类的泛型参数数组
   */
  public static <S, T extends S> Type[] getGenericsBySuper(Class<T> clazz, Class<S> superClass) {
    Optional<ParameterizedType> parameterizedType = Arrays.stream(clazz.getGenericInterfaces())
            .filter(it -> it instanceof ParameterizedType)
            .map(it -> (ParameterizedType) it)
            .filter(it -> it.getRawType() == superClass)
            .findFirst();
    if (!parameterizedType.isPresent()) return new Type[0];
    return parameterizedType.get().getActualTypeArguments();
  }

  /**
   * 获取属性值
   *
   * @param object 对象
   * @param field  属性
   * @return 返回该对象的该属性值
   */
  public static Object getFieldValue(Object object, Field field) {
    field.setAccessible(true);
    try {
      return field.get(object);
    } catch (ReflectiveOperationException e) {
      throw new ReflectWeakException(e);
    }
  }

  /**
   * 设置属性值
   *
   * @param object   对象
   * @param field    属性
   * @param newValue 属性新值
   */
  public static void setFieldValue(Object object, Field field, Object newValue) {
    field.setAccessible(true);
    try {
      field.set(object, newValue);
    } catch (ReflectiveOperationException e) {
      throw new ReflectWeakException(e);
    }
  }

  /**
   * 是否可以赋值
   *
   * @param superclass 接收值父类
   * @param subclass   值子类
   * @return 如果是返回true，否则返回false
   */
  public static boolean isAssignableFrom(Class<?> superclass, Class<?> subclass) {
    return getPackClass(superclass).isAssignableFrom(getPackClass(subclass));
  }

  /**
   * 为基本类型获取包装类
   *
   * @param clazz 要包装的类
   * @return 如果是基本类型则返回包装类，否则返回原类
   */
  public static Class<?> getPackClass(Class<?> clazz) {
    if (!clazz.isPrimitive()) return clazz;

    if (clazz == byte.class) return Byte.class;
    if (clazz == short.class) return Short.class;
    if (clazz == int.class) return Integer.class;
    if (clazz == long.class) return Long.class;
    if (clazz == float.class) return Float.class;
    if (clazz == double.class) return Double.class;
    if (clazz == char.class) return Character.class;
    if (clazz == boolean.class) return Boolean.class;

    throw new ReflectWeakException("无法包装未知的基本类型:" + clazz);
  }

  /**
   * 获取类中所有属性(包括父类)
   *
   * @param clazz 类
   * @return 返回该类包括父类的所有属性
   */
  public static List<Field> getClassFieldList(Class<?> clazz) {
    LinkedList<Field> fieldList = new LinkedList<>();
    Class doClass = clazz;
    while (doClass != null) {
      Field[] fields = doClass.getDeclaredFields();
      fieldList.addAll(0, Arrays.asList(fields)); //将父类的属性加载列表最前面
      doClass = doClass.getSuperclass();
    }
    return fieldList;
  }

  /**
   * 获取类中所有属性(包括父类)
   *
   * @param object 对象
   * @return 返回该类包括父类的所有属性
   */
  public static List<Field> getObjectFieldList(Object object) {
    return getClassFieldList(getTargetClass(object));
  }

  /**
   * 获取代理对象的目标类
   */
  @NotNull
  public static Class<?> getTargetClass(@NotNull Object proxy) {
    Object object = getTargeObject(proxy);

    if (object instanceof Annotation) return ((Annotation) object).annotationType(); //如果目标为注解

    if (object.getClass().getSimpleName().contains("CGLIB$$")) return object.getClass().getSuperclass(); //如果是cglib代理类

    return object.getClass();
  }

  /**
   * 获取代理对象的目标对象
   *
   * @param proxy 代理对象
   * @return 如果提供参数为代理对象则获取其目标对象, 否则将返回原对象
   */
  @NotNull
  public static <T> T getTargeObject(@NotNull T proxy) {
    if (proxy instanceof ScopedObject) {
      //如果实现了 spring 的被作用域控制管理接口
      @SuppressWarnings("unchecked")
      T wTarget = (T) ((ScopedObject) proxy).getTargetObject();
      return getTargeObject(wTarget);
    }

    if (proxy instanceof Advised) {
      //如果实现了spring 的被aop增强管理接口
      try {
        @SuppressWarnings("unchecked")
        T target = (T) ((Advised) proxy).getTargetSource().getTarget();
        if (target != null) return target;
      } catch (Exception e) {
        throw new UndeclaredThrowableException(e);
      }
    }

    return proxy;
  }

  /**
   * 通过字符串构造一个对象
   *
   * @param targetType 要构造的目标类型
   * @param source     源字符串
   * @param <T>        目标类型
   * @return 如果成功返回目标对象
   */
  @Nullable
  @SuppressWarnings("unchecked")
  public static <T> T newByString(@NotNull Class<T> targetType, @Nullable String source) {
    if (source == null) {
      if (targetType.isPrimitive()) throw new ReflectWeakException("null不能转换为基本类型:" + targetType);
      return null;    //如果源为null,则返回null
    }

    if (isAssignableFrom(targetType, String.class)) return (T) source; //如果目标可以赋值字符串
    if (isAssignableFrom(targetType, Number.class)) return (T) new BigDecimal(source); //如果目标接收数值

    {
      //date系列
      if (isAssignableFrom(targetType, Date.class)) {
        Date date = LangUtils.ignore(Exception.class, () -> DatetimeUtils.parseDate(source));
        if (date != null) return (T) new Timestamp(date.getTime());
      }

      if (isAssignableFrom(Date.class, targetType)) {
        //目标类型为data子类(Timestamp、Time)系列
        Date date = LangUtils.ignore(Exception.class, () -> DatetimeUtils.parseDate(source));
        if (date != null) {
          if (isAssignableFrom(Timestamp.class, targetType)) return (T) new Timestamp(date.getTime());
          if (isAssignableFrom(java.sql.Date.class, targetType)) return (T) new java.sql.Date(date.getTime());
          if (isAssignableFrom(Time.class, targetType)) return (T) new Time(date.getTime());
        }
      }
    }

    {
      //jdk8 localDateTime 系列
      if (isAssignableFrom(targetType, LocalDateTime.class)) {
        LocalDateTime datetime = LangUtils.ignore(Exception.class, () -> DatetimeUtils.parseLocalDateTime(source));
        if (datetime != null) return (T) datetime;
      }
      if (isAssignableFrom(targetType, LocalDate.class)) {
        LocalDateTime datetime = LangUtils.ignore(Exception.class, () -> DatetimeUtils.parseLocalDateTime(source));
        if (datetime != null) return (T) datetime.toLocalDate();
      }
      if (isAssignableFrom(targetType, LocalTime.class)) {
        LocalDateTime datetime = LangUtils.ignore(Exception.class, () -> LocalDateTime.parse(source));
        if (datetime != null) return (T) datetime.toLocalTime();
      }
    }

    {
      //使用valueOf方法转换
      Method valueOf;
      try {
        valueOf = getPackClass(targetType).getDeclaredMethod("valueOf", String.class);
        if (Modifier.isStatic(valueOf.getModifiers()) && isAssignableFrom(targetType, valueOf.getReturnType())) {
          try {
            valueOf.setAccessible(true);
            return (T) valueOf.invoke(null, source);
          } catch (ReflectiveOperationException e) {
            throw new ReflectWeakException(e);
          }
        }
      } catch (NoSuchMethodException e) {
        logger.debug(String.format("找不到valueOf方法:%s", e));
      }
    }

    if (!Modifier.isAbstract(getPackClass(targetType).getModifiers())) {
      //使用构造方法转换
      Constructor constructor;
      try {
        constructor = getPackClass(targetType).getConstructor(String.class);
        if (!Modifier.isPrivate(constructor.getModifiers())) {
          //
          try {
            constructor.setAccessible(true);
            return (T) constructor.newInstance(source);
          } catch (ReflectiveOperationException e) {
            throw new ReflectWeakException(e);
          }
        }
      } catch (NoSuchMethodException e) {
        logger.debug(String.format("找不到构造方法:%s", e));
      }
    }

    throw new ReflectWeakException("无法将字符串转换为:" + targetType);
  }
}
